// ======================================================================
// Hell Lua Bus for ATtiny2313/ATtiny2313A
//
// Copyright 2012 Hell-Prototypes
//
// http://code.google.com/p/hell-prototypes/
//
// This is free software, licensed under the terms of the GNU General
// Public License as published by the Free Software Foundation.
// ======================================================================

#include "../usbtiny/usb.h"
#include "../usbtiny/def.h"
#include "i2c.h"
#include "spi.h"

#define USB_PORT_WR			0xC0
#define USB_PORT_RD			0xC1
#define USB_OE_SET			0xC2

//#define USB_PINS_SET		0xC3
//#define USB_PINS_CLR		0xC4
#if HLB_RS232_EN
#define USB_RS232_OPEN		0xD0
#define USB_RS232_RW		0xD1
#define USB_RS232_CLOSE		0xD2
#endif

#define USB_FW_VER			0xD3
#define USB_12V_CTRL		0xD4

#define USB_I2C_Start		0xD5
#define USB_I2C_Stop		0xD6
#define USB_I2C_Write		0xD7
#define USB_I2C_Read		0xD8

#define USB_SPI_INIT		0xD9
#define USB_SPI_PLAY_1BYTE	0xDA

#define USB_ICSP_RD			0xDB
#define USB_ICSP_WR			0xDC

#define IO_CMD_NONE			0x00
#define IO_CMD_RS232		0x01

#define BAUND_RATE			9600
#define BAUND_RATE_VALUE	(12000000/8/BAUND_RATE - 1)

#define FW_VER				1

#if HLB_RS232_EN
static byte_t g_io_cmd;
static uint_t g_time_out;
#endif
// ----------------------------------------------------------------------
// Handle a non-standard SETUP packet.
// ----------------------------------------------------------------------
extern	byte_t	usb_setup ( byte_t data[8] )
{
	byte_t data_2 = data[2];
	byte_t data_3 = data[3];
#if HLB_RS232_EN
	g_io_cmd = IO_CMD_NONE;
	byte_t data_4 = data[4];
#endif

	switch(data[1]) {
	case USB_PORT_WR://Write port
		PORTB = data_2;
		PORTD = (PORTD & USB_MASK) | (data_3 & (~USB_MASK));
		break;
	case USB_PORT_RD://Read port
		data[0] = PINB;
		data[1] = PIND;
		return 2;
		break;
//	case USB_PINS_SET:
//		PORTB |= data[2];
//		PORTD |= data[3] & (~USB_MASK);
//		break;
//	case USB_PINS_CLR:
//		PORTB &= data[2];
//		PORTD &= data[3] | (USB_MASK);
//		break;
	case USB_OE_SET://Set output enable
		DDRB = data_2;
		DDRD = (DDRD & USB_MASK) | (data_3 & (~USB_MASK));
		break;
#if HLB_RS232_EN
	case USB_RS232_OPEN:
		/*  Set baud rate  */
		UBRRL = data_2;
		UBRRH = data_3;
		/* Parity */
		UCSRC = data_4;
/*
		if(data[4] > 0) {
			if(data[4] == 1) {//odd
				UCSRC |= (3 << UPM0);
			} else {//even
				UCSRC = (UCSRC & (~(3 << UPM0))) | (2 << UPM0);
			}
		} else {
			UCSRC &= ~(3 << UPM0);
		}
*/
		/*  Enable receiver and transmitter */
		UCSRB = (1<<RXEN)|(1<<TXEN);
		break;
	case USB_RS232_RW://usb_out: write, usb_in: read
		g_io_cmd = IO_CMD_RS232;
		if ( data_4 != 0 ) {
			g_time_out = *((uint_t *)(&data[2]));//data[2] + ((uint_t)data[3] << 8);
			return 0xFF;
		}
		break;
	case USB_RS232_CLOSE:
		/*  Disable receiver and transmitter */
		UCSRB = UCSRB & (~((1<<RXEN)|(1<<TXEN)));
		break;
#endif //#if HLB_RS232_EN
	case USB_FW_VER:
		data[0] = FW_VER;
		return 1;
		break;
	case USB_12V_CTRL:
		if(data_2 != 0) {
			PORT_SET(A, 2);
		} else {
			PORT_CLR(A, 2);
		}
		break;
	case USB_I2C_Start:
		i2c_start();
		break;
	case USB_I2C_Stop:
		i2c_stop();
		break;
	case USB_I2C_Write:
		data[0] = i2c_write(data_2);
		return 1;
		break;
	case USB_I2C_Read:
		data[0] = i2c_read(data_2);
		return 1;
		break;
	case USB_SPI_INIT:
		spi_init(data_2);
		break;
	case USB_SPI_PLAY_1BYTE:
		data[0] = spi_play_one_byte(data_2);
		return 1;
		break;
	case USB_ICSP_RD:
		data[0] = icsp_read();
		return 1;
		break;
	case USB_ICSP_WR:
		icsp_write(data_2, data_3);
		break;
	default:
		break;
	}
	return 0;
}

#if HLB_RS232_EN
// ----------------------------------------------------------------------
// Handle an IN packet.
// ----------------------------------------------------------------------
extern byte_t usb_in ( byte_t* data, byte_t len )
{
	byte_t ret = 0;
	uint_t time_out;

	if(g_io_cmd == IO_CMD_RS232) {//RS232 read
		time_out = g_time_out;
		while(ret<len ) {
			if(time_out > 0) {
				time_out--;
				if(time_out == 0) {
					break;
				}
			}
			/*  Wait for data to be received  */
			if(UCSRA & (1<<RXC)) {
				/*  Get and return received data from buffer  */
				*data++ = UDR;
				ret++;
				time_out = g_time_out;
			}
		}
	}
	
	return ret;
}

// ----------------------------------------------------------------------
// Handle an OUT packet.
// ----------------------------------------------------------------------
extern void usb_out ( byte_t* data, byte_t len )
{
	if(g_io_cmd == IO_CMD_RS232) {//RS232 write
		while(len--) {
			/*  Wait for empty transmit buffer  */
			while ( !( UCSRA & (1<<UDRE)) );
			/*  Put data into buffer, sends the data */
			UDR = *data++;
		}
	}
}
#endif //#if HLB_RS232_EN
// ----------------------------------------------------------------------
// Main
// ----------------------------------------------------------------------
extern	int	main ( void )
{
	/*  Set baud rate  */
	//UBRRH = (unsigned char)(BAUND_RATE_VALUE>>8);
	//UBRRL = (unsigned char)BAUND_RATE_VALUE;

	/*  Set frame format: 8data, 1stop bit */
	//UCSRC = (3<<UCSZ0);//use reset value
	
	UCSRA |= 1 << U2X;

	//USB RESET PIN AS GPIO
	DDRA = DDRA | (1<<DDRA2);
	//PORT_CLR(A, 2); //use reset value

	usb_init();
	for	( ;; )
	{
		usb_poll();
	}
}
